/*
 *  mih_net_sap.c
 *  MIHF
 *
 *  Created by injae song on 11. 3. 5..
 *  Copyright 2011 KyungHee Univ. All rights reserved.
 *
 */

#include "mih_net_sap.h"

int init_mih_net_sap(mihf *mih, server_type type) {
	
	warn("Init MIH_NET_SAP for remote MIHF\n");
	
	int sfd=-1;
	
	struct sockaddr_in inaddr;
	int len = 1;
	int res;
	
	if(type == TCP) {
		
		sfd = socket(PF_INET, SOCK_STREAM, 0);
		if(sfd == -1) {
			exit(1);
		}
		
	    if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&len,sizeof(len))==-1) {
	        exit(2);
	    }
		
	    if(fcntl(sfd, F_SETFL, O_NONBLOCK) == -1) {
 	       	exit(2);
	    }
		
	} else if(type == UDP) {
		sfd = socket(AF_INET, SOCK_DGRAM, 0);
		if(sfd == -1) {
			exit(1);
		}
	}
	
	memset((char *) &inaddr, 0, sizeof(inaddr));
	
	inaddr.sin_family = AF_INET;
	inaddr.sin_family = AF_INET;
	inaddr.sin_addr.s_addr = INADDR_ANY;
	inaddr.sin_port = htons(MIH_NET_SAP_PORT);
	res = bind(sfd, (struct sockaddr *)&inaddr, sizeof (inaddr));
	if (res == -1) {
		exit(1);
	} 
	
	if(type == TCP) {
		/* Listen */
		res = listen(sfd, MAX_LISTEN);
		if (res == -1) {
			exit(1);
		} 
	}
	
	event_set(&(mih->ev_accept_net_tcp_peer), sfd, EV_READ | EV_PERSIST, mih_net_connection_accept, (void *)mih);
	
	event_add(&(mih->ev_accept_net_tcp_peer), NULL);

	
	warn( "MIH_NET_SAP listening on port %d\n", MIH_NET_SAP_PORT);
	
	return 0;
}

void mih_net_connection_accept(int fd, short event , void *arg) {
	
	mihf *mih = arg;
	
	//	struct remote_mih_connection *client;
	mihf_peer *peer;
	
	struct sockaddr_storage ss;
	socklen_t addrlen = sizeof(ss);
	int nfd;
	
	nfd = accept(fd, (struct sockaddr *)&ss, &addrlen);
	if (nfd == -1) {
		if (errno != EAGAIN && errno != EINTR) {
			
			warn("bad accept\n");
		}
		return;
	}
	
	
	if (evutil_make_socket_nonblocking(nfd) < 0)
		return;
	
	warn("new remote mih on fd = %d\n", nfd);
	
	peer= malloc(sizeof(mihf_peer));
	
	if((peer == NULL))
		exit(0);
	struct bufferevent* buf = bufferevent_new(nfd,
											  mih_net_connection_read_handler,
											  mih_net_connection_write_handler,
											  mih_net_connection_error_handler,
											  (void *) peer);
	
	if(buf == NULL) {
		exit(0);
	}
	
	bufferevent_enable(buf, EV_READ);
	peer->fd = nfd;
	peer->evb = buf;
	peer->mih = mih;
	

	
	list_ins_next(&mih->Connect_table, list_tail(&mih->Connect_table), (void*)peer);
	
	return;
}

void mih_net_send(char *addr, int port, char *msg, int size, mihf *mih) {
	struct bufferevent *bev;
	mihf_peer *conn;
	
	int fd = mih_connect(addr, port); 
	if(fd == -1) {
		warn("Connection to remote remote peer failed\n");	
		return;
	} 
	
	conn = malloc(sizeof(mihf_peer));
	if((conn == NULL))
		exit(0);
	
	bev = bufferevent_new(fd,
						  mih_net_connection_read_handler,
						  mih_net_connection_close_after_write_handler,
						  mih_net_connection_error_handler,
						  (void *) conn);
	
	if(bev == NULL) {
		exit(0);
	}
	
	bufferevent_enable(bev, EV_READ);		
	conn->fd =fd;
	conn->evb = bev;
	conn->mih = mih;
	
	list_ins_next(&mih->Connect_table, list_tail(&mih->Connect_table), (void*)conn);
	
	warn("msg length = %d\n", size);
	
	int res = bufferevent_write(bev, msg, size);
	if(res != 0) {
		
		warn("bufferevent write failed\n");
	}	
	
}

/* handlers for MIH clients */
void mih_net_connection_read_handler(struct bufferevent *bev, void *arg) {
	
    mihf_peer *conn = arg;
	mih_message* mih_msg;
	char *buffer;
	
	size_t len = EVBUFFER_LENGTH(bev->input);
	buffer = malloc(len);
	bzero(buffer, len);
	
	// Read MIH header
	bufferevent_read(bev, buffer, len);
	
    mih_msg = mih_message_parse(buffer);
	
    mih_protocol_handle(mih_msg, conn);
	
	return;
}

void mih_net_connection_write_handler(struct bufferevent *bev , void *arg) {
	warn("# write done\n");
	
}

void mih_net_connection_close_after_write_handler(struct bufferevent *bev , void *arg) {
	struct mih_connection *s; 
	s = arg;
	warn("# write done, closing\n");
	//free_mih_connection(s);
	return;
	
}

int mih_connect(char *addr, int port) {
	int res = 0;
	struct sockaddr_in remote;
	int sock;
	struct hostent *he;
    
	he = gethostbyname(addr);
	sock = socket(AF_INET, SOCK_STREAM,0);
	remote.sin_family = AF_INET;
	remote.sin_port = htons(port);
	remote.sin_addr = *((struct in_addr *)he->h_addr);
    
	res = connect(sock, (struct sockaddr *)&remote, sizeof(remote));
	if(res == -1) {
		perror("Socket connection error");
		close(sock);
		return res;
	} else {
		return sock;
	}
    
}

void mih_net_connection_error_handler(struct bufferevent *bev, short what, void *arg) {
	
    mihf_peer *s; 
	
	s = arg;
    
	if (what & EVBUFFER_EOF)
		warn("#%d client close\n", s->fd);
	else if (what == (EVBUFFER_ERROR | EVBUFFER_READ))
		warn("#%d client reset connection\n", s->fd);
	else if (what & EVBUFFER_TIMEOUT)
		warn("#%d client timeout\n", s->fd);
	else if (what & EVBUFFER_WRITE)
		warn("#%d client write error\n", s->fd);
	else
		warn("#%d abnormal client error\n", s->fd);
    
//	free_mih_connection(s->mih,s->fd);
	return;
    
}


